package compilador.lexico;

import java.util.Scanner;

/**
 * Gerencia a leitura da entrada fornecida ao analisador léxico.
 *
 * @author Carlos H V Pinto
 * @author Lucas S Bueno
 * @author Paulo G L Freire
 */
public final class LeitorEntrada {

    private Scanner leitor;     //objeto que executa a leitura do arquivo de entrada
    private String buffer;     //buffer que guarda uma linha do arquivo de entrada.
    private int linha;      //contador que mostra o número da linha que está no buffer. 
    private int ponteiro;   //número que marca a posição de leitura dentro do buffer.

    /*
     * Cria um leitor de entrada inicializando o número da linha e o ponteiro, e
     * guardando no buffer a primeira linha do arquivo.
     */
    public LeitorEntrada() {
        //leitor recebe como parâmetro a entrada padrão (args[0])
        leitor = new Scanner(System.in);
        linha = 0;
        ponteiro = 0;
        buffer = new String();
        proximaLinha();
    }

    //retorna a linha atual que está sendo lida do arquivo (armazenada no buffer).
    public int getLinha() {
        return linha;
    }

    //verifica se o arquivo terminou (não existem mais linhas e a última linha foi lida inteiramente).
    public boolean terminou() {
        return ((!leitor.hasNext()) && (ponteiro > buffer.length()));
    }

    //avança a leitura para a próxima linha não vazia.
    public void proximaLinha() {
        if (!terminou()) {
            do {
                try {
                    buffer = leitor.nextLine();
                    linha++;
                } catch (Exception e) {
                    ponteiro++;
                }                
            } while (buffer.isEmpty() && !terminou());
        }
    }

    //busca o próximo caracter a ser lido. se o arquivo já acabou retorna o caracter nulo. 
    public char proximoCaracter() {
        if (!terminou()) {
            char c;  //caracter a ser retornado

            /*
             * ponteiro dentro das dimensões do buffer. atribui o valor do
             * caracter que será retornado utilizando a posição do ponteiro.
             * Após isto avança a posição do ponteiro para o próximo caracter.
             */
            if (ponteiro < buffer.length()) {
                c = buffer.charAt(ponteiro);
                ponteiro++;
            } /*
             * o ponteiro de leitura neste caso ultrapassou em 1 o limite da
             * dimensão do buffer. o retorno será o caracter de nova linha
             * para facilitar o reconhecimento de erros de comentários não
             * fechados e cadeias literais não fechadas.
             */ else if (ponteiro == buffer.length()) {
                c = '\n';
                ponteiro++;
            } /*
             * neste ponto a linha inteira já foi lida, inclusive com a
             * saída do caracter de nova linha. então o ponteiro é zerado, a
             * próxima linha é trazida para o buffer, o caracter de retorno
             * é buscado e o ponteiro avança em uma posição.
             */ else {
                ponteiro = 0;
                proximaLinha();
                c = buffer.charAt(ponteiro);
                ponteiro++;
            }

            return c;
        }

        return '\0';
    }

    //retrocede o ponteiro de leitura em uma posição.
    public void retrocedeEntrada() {
        if (ponteiro > 0) {
            ponteiro--;
        }
    }

    //retrocede o ponteiro de leitura em uma quantidade de posições determinada.
    public void retrocedeEntrada(int quantidade) {
        if (ponteiro > (quantidade - 1)) {
            ponteiro -= quantidade;
        }
    }
}
